import { isVNode, reactive, provide, getCurrentInstance } from "vue";
// 将嵌套的虚拟节点数组数组化
// 使用递归的方式遍历嵌套的虚拟节点数组，将所有的虚拟节点收集到一个一维数组中。如果虚拟节点有子节点或者子组件的子树，也会递归遍历这些子节点
function flattenVNodes(children) {
    const result = [];
    const traverse = (children2) => {
        if (Array.isArray(children2)) {
            children2.forEach((child) => {
                var _a;
                if (isVNode(child)) {
                    result.push(child);
                    if ((_a = child.component) == null ? void 0 : _a.subTree) {
                        result.push(child.component.subTree);
                        traverse(child.component.subTree.children);
                    }
                    if (child.children) {
                        traverse(child.children);
                    }
                }
            });
        }
    };
    traverse(children);
    return result;
}

// 在虚拟节点数组中查找指定虚拟节点的索引
// 首先尝试使用 indexOf 方法查找索引，如果找不到，则通过比较虚拟节点的 type 和 key 来查找索引
var findVNodeIndex = (vnodes, vnode) => {
    const index = vnodes.indexOf(vnode);
    if (index === -1) {
        return vnodes.findIndex(
            (item) =>
                vnode.key !== void 0 &&
                vnode.key !== null &&
                item.type === vnode.type &&
                item.key === vnode.key
        );
    }
    return index;
};
// 对收集到的子组件进行排序，确保子组件的顺序与模板中的顺序一致
// 首先将父组件的子节点扁平化，然后根据虚拟节点的索引对内部子组件和公共子组件进行排序。
function sortChildren(parent, publicChildren, internalChildren) {
    const vnodes = flattenVNodes(parent.subTree.children);
    internalChildren.sort(
        (a, b) =>
            findVNodeIndex(vnodes, a.vnode) - findVNodeIndex(vnodes, b.vnode)
    );
    const orderedPublicChildren = internalChildren.map((item) => item.proxy);
    publicChildren.sort((a, b) => {
        const indexA = orderedPublicChildren.indexOf(a);
        const indexB = orderedPublicChildren.indexOf(b);
        return indexA - indexB;
    });
}
// 创建一个用于管理子组件的上下文，并提供添加和移除子组件的方法
/*
    1、创建两个响应式数组 publicChildren 和 internalChildren，分别用于存储公共子组件实例和内部子组件信息。
    2、获取当前组件实例 parent。
    3、定义 linkChildren 函数，该函数接受一个可选的 value 参数，用于扩展提供的上下文。
    4、在 linkChildren 函数内部，定义 link 和 unlink 方法，分别用于添加和移除子组件。
    5、使用 provide 方法将上下文提供给子组件。
*/
export default function useChildren(key) {
    const publicChildren = reactive([]);
    const internalChildren = reactive([]);
    const parent = getCurrentInstance();
    const linkChildren = (value) => {
        const link = (child) => {
            if (child.proxy) {
                internalChildren.push(child);
                publicChildren.push(child.proxy);
                sortChildren(parent, publicChildren, internalChildren);
            }
        };
        const unlink = (child) => {
            const index = internalChildren.indexOf(child);
            publicChildren.splice(index, 1);
            internalChildren.splice(index, 1);
        };
        provide(
            key,
            Object.assign(
                {
                    link,
                    unlink,
                    children: publicChildren,
                    internalChildren,
                },
                value
            )
        );
    };
    return {
        children: publicChildren,
        linkChildren,
    };
}
